home *** CD-ROM | disk | FTP | other *** search
/ Mac Expert 1995 Winter / Mac Expert - Winter 95.iso / Les fichiers / Communications / Internet / TurboTCP 2.1 ƒ / TurboTCP core / CTCPStream.cp < prev    next >
Encoding:
Text File  |  1995-01-06  |  25.6 KB  |  965 lines  |  [TEXT/MMCC]

  1. //
  2. // CTCPStream.cp
  3. //
  4. //    TurboTCP library
  5. //    TCP stream class
  6. //
  7. //    Copyright © 1993-95, FrostByte Design / Eric Scouten
  8. //
  9.  
  10. #include "CTCPStream.h"
  11.  
  12. #include "CTCPAsyncCall.h"
  13. #include "CTCPResolverCall.h"
  14.  
  15. #if !TurboTCP_UH2
  16.     #define TCPIOCompletionUPP TCPIOCompletionProc
  17. #endif
  18.  
  19. #if TurboTCP_CFM
  20.     UniversalProcPtr CTCPStream::notifyProcUPP =
  21.         NewRoutineDescriptor((ProcPtr) CTCPStream::NotifyProc, uppTCPNotifyProcInfo, GetCurrentISA());
  22. #endif
  23.  
  24.  
  25. //***********************************************************
  26.  
  27. CTCPStream::CTCPStream
  28.     (CTCPEndpoint&    theEndpoint,            // endpoint object which is using this stream
  29.     long                recBufferSize,            // size of the receive buffer we need; min 4K enforced
  30.     short            autoReceiveSize,        // number of entries in RDS for auto-receive
  31.     short            autoReceiveNum)        // number of auto-receive calls to issue at once
  32.  
  33.     // Create a new TCP stream. Allocates the receive buffer (if there’s enough memory).
  34.     
  35.     // The stream can be configured to automatically receive data. If auto-receive is used,
  36.     // the stream object will automatically issue TCPNoCopyRcv calls as needed and respond to
  37.     // completion notifications when data arrives; data will be returned by the HandleDataArrived
  38.     // method or by the receive bypass procedure (see InstallRcvBypassProc).
  39.     
  40.     // If auto-receive is used, you should not send NoCopyRcv and Rcv messages to this stream.
  41.     // They may conflict with the auto-receive calls. Auto-receive cannot be turned on or off
  42.     // once the stream object is created.
  43.  
  44. : itsEndpoint(&theEndpoint), qNotifyEntry(this), qDisposeEntry(this)
  45. {
  46.  
  47.     // clear all of our variables
  48.     
  49.     macTCPStream = nil;
  50.     itsBuffer = nil;
  51.     itsAsyncCalls.qHead = itsAsyncCalls.qTail = nil;    
  52.     hasSessionOpen = pendingOpen = pendingClose = remoteClose
  53.         = pendingAbort = pendingNotify = pendingDispose = false;
  54.     
  55.     itsBufferSize = 0L;
  56.     rcvUrgent = false;
  57.     
  58.     itsULPtimeout = 0;
  59.     itsULPaction = 0;
  60.     itsValidityFlag = 0;
  61.     itsCommandTimeoutValue = 0;
  62.     itsTosFlags = 0;
  63.     itsPrecedence = 0;
  64.     itsDontFrag = 0;
  65.     itsTimeToLive = 0;
  66.     itsSecurity = 0;
  67.     itsOptionCnt = 0;
  68.     sendNextUrgent = 0;
  69.     sendNextPush = false;
  70.  
  71.     notifClosing = false;
  72.     notifTimeout = false;
  73.     notifTerminate = false;
  74.     notifDataArrived = false;
  75.     notifUrgent = false;
  76.     notifICMP = false;
  77.     disposeOnTerminate = false;
  78.     receivedClose = false;
  79.     receivedTerminate = false;
  80.     
  81.     if (autoReceiveSize > autoReceiveMax)
  82.         itsAutoReceiveSize = autoReceiveMax;
  83.     else
  84.         itsAutoReceiveSize = autoReceiveSize;
  85.     if (autoReceiveNum < 1)
  86.         itsAutoReceiveNum = 1;
  87.     else
  88.         itsAutoReceiveNum = autoReceiveNum;
  89.  
  90.     qNotifyEntry.qType = notifyStream;
  91.     qDisposeEntry.qType = disposeStream;
  92.  
  93.  
  94.     // create the receive buffer
  95.     
  96.     if (recBufferSize < minReceiveSize)                    // if caller requested a buffer that is too small,
  97.         recBufferSize = minReceiveSize;                //    beef it up to acceptable size (4K)
  98.     itsBuffer = ::NewHandle(recBufferSize);
  99.     ThrowIfNil_(itsBuffer);
  100.     itsBufferSize = recBufferSize;
  101.  
  102.  
  103.     //
  104.     // Don’t create TCP stream here. Therefore, if a session is never opened and the program
  105.     // crashes (or ExitToShell is used by the programmer), MacTCP will remain stable.
  106.     //
  107.     
  108. }
  109.  
  110.  
  111. //***********************************************************
  112.  
  113. void CTCPStream::Dispose()
  114.  
  115.     // Get rid of a TCP stream. Deallocates the receive buffer. If a session is still open,
  116.     // issues an Abort command, then postpones its disposal so completion routines still
  117.     // have a valid object to call.
  118.  
  119. {
  120.     TCPiopb theReleaseParam;
  121.  
  122.     // assume that the endpoint object is going away also
  123.  
  124.     itsEndpoint = nil;
  125.  
  126.  
  127.     // we’re no longer in delayed disposal queue
  128.  
  129.     pendingDispose = false;
  130.  
  131.  
  132.     // initiate a graceful close if necessary
  133.     
  134.     if (hasSessionOpen && !receivedClose) {
  135.         Close();
  136.         disposeOnTerminate = true;
  137.         return;
  138.     }
  139.  
  140.  
  141.     // abort any connections that are yet to open
  142.     
  143.     if (pendingOpen) {
  144.         Abort();
  145.         disposeOnTerminate = true;
  146.         return;
  147.     }
  148.  
  149.  
  150.     // if calls are still outstanding, wait a while
  151.     
  152.     if (itsAsyncCalls.qHead) {
  153.         if ((macTCPStream) && pendingOpen) {                // get rid of the thing…
  154.             Abort();
  155.             PostponeDispose();
  156.             return;
  157.         }
  158.         if ((macTCPStream) && pendingOpen) {
  159.             DoSyncCall(TCPRelease, &theReleaseParam);        // perhaps this will shake it loose
  160.             macTCPStream = nil;                        // 2.0b5 bug fix: remember that we killed the stream
  161.         }
  162.         PostponeDispose();
  163.         return;
  164.     }
  165.  
  166.  
  167.     // if connection is still there, release stream to see if that shakes things loose
  168.     
  169.     if (macTCPStream && hasSessionOpen && !receivedTerminate) {
  170.         DoSyncCall(TCPRelease, &theReleaseParam);
  171.         macTCPStream = nil;
  172.         PostponeDispose();
  173.         return;        
  174.     }
  175.  
  176.  
  177.     // make sure that terminate notice has been received
  178.     
  179.     if (hasSessionOpen && !receivedTerminate) {
  180.         PostponeDispose();
  181.         return;
  182.     }    
  183.  
  184.  
  185.     // release the stream from MacTCP
  186.     
  187.     if (macTCPStream)
  188.         DoSyncCall(TCPRelease, &theReleaseParam);
  189.     CTCPDriver::gTCPDriver->RemoveActiveStream(this);
  190.  
  191.  
  192.     // release the receive buffer
  193.  
  194.     ::DisposeHandle(itsBuffer);
  195.     macTCPStream = nil;
  196.     itsBufferSize = 0L;
  197.  
  198.     delete this;
  199. }
  200.  
  201.  
  202. // -- basic user TCP calls --
  203.  
  204. //***********************************************************
  205.  
  206. void CTCPStream::OpenConnection
  207.     (Boolean    passive,            // true for passive open; false for active open
  208.     ip_addr    theRemoteIP,        // IP address of remote host (0 matches any host on listen)
  209.     b_16        theRemotePort,        // TCP port of remote host (0 matches any port on listen)
  210.     b_16        theLocalPort)        // TCP port of local machine (0 for randomly-assigned port)
  211.  
  212.     // Opens a TCP session (either passive or active). Note that MacTCP seems to fail when
  213.     // non-zero values are used for theRemoteIP and theRemotePort.
  214.  
  215. {
  216.     TCPiopb    theCreateParam;
  217.     TCPiopb    theOpenParam;
  218.     OSErr    theResult;
  219.  
  220.     
  221.     // ensure that MacTCP is awake & ready for us
  222.     
  223.     if (!(CTCPDriver::gTCPDriver->CheckTCPDriver()))
  224.         ThrowOSErr_(noTCPError);
  225.  
  226.  
  227.     // if the stream hasn’t been created yet, create one now
  228.     
  229.     if (!macTCPStream) {
  230.         ::MoveHHi(itsBuffer);                            // lock the receive buffer
  231.         ::HLock(itsBuffer);
  232.                 
  233.         theCreateParam.csParam.create.rcvBuff = *itsBuffer;
  234.         theCreateParam.csParam.create.rcvBuffLen = itsBufferSize;
  235.         #if TurboTCP_CFM
  236.             theCreateParam.csParam.create.notifyProc = (TCPNotifyUPP) notifyProcUPP;
  237.         #else
  238.             theCreateParam.csParam.create.notifyProc = &NotifyProc;
  239.         #endif
  240.         theCreateParam.csParam.create.userDataPtr = (Ptr) this;
  241.         
  242.         macTCPStream = (Ptr) 1;                        // preempt the check for no stream present
  243.         theResult = DoSyncCall(TCPCreate, &theCreateParam);
  244.         
  245.         if (theResult == noErr) {                        // did we get a valid stream?
  246.             CTCPDriver::gTCPDriver->RegisterActiveStream(this);    //    yes, register in global stream list
  247.             macTCPStream = (Ptr) theCreateParam.tcpStream;
  248.         } else {
  249.             macTCPStream = nil;
  250.             HandleOpenFailed(theResult);
  251.         }
  252.     }
  253.  
  254.  
  255.     // don’t allow open if already opening
  256.     
  257.     if (!(hasSessionOpen || pendingOpen)) {
  258.  
  259.         // fill in parms to open call
  260.         
  261.         theOpenParam.csParam.open.ulpTimeoutValue = itsULPtimeout;
  262.         theOpenParam.csParam.open.ulpTimeoutAction = itsULPaction;
  263.         theOpenParam.csParam.open.validityFlags = itsValidityFlag;
  264.         theOpenParam.csParam.open.commandTimeoutValue = itsCommandTimeoutValue;
  265.         theOpenParam.csParam.open.remoteHost = theRemoteIP;
  266.         theOpenParam.csParam.open.remotePort = theRemotePort;
  267.         theOpenParam.csParam.open.localPort = theLocalPort;
  268.         theOpenParam.csParam.open.tosFlags = itsTosFlags;
  269.         theOpenParam.csParam.open.precedence = itsPrecedence;
  270.         theOpenParam.csParam.open.dontFrag = itsDontFrag;
  271.         theOpenParam.csParam.open.timeToLive = itsTimeToLive;
  272.         theOpenParam.csParam.open.security = itsSecurity;
  273.         theOpenParam.csParam.open.optionCnt = itsOptionCnt;
  274.         ::BlockMoveData(&itsOptions, &theOpenParam.csParam.open.options, 40);
  275.         theOpenParam.csParam.open.userDataPtr = (Ptr) this;
  276.         pendingOpen = true;
  277.         receivedClose = receivedTerminate = false;
  278.     
  279.         // perform the call
  280.         
  281.         theResult = DoAsyncCall((passive ? TCPPassiveOpen : TCPActiveOpen), &theOpenParam);
  282.         if (theResult)
  283.             HandleOpenFailed(theResult);
  284.         
  285.         // pull IP address/port info from remote host
  286.         
  287.         itsRemoteIP = theOpenParam.csParam.open.remoteHost;
  288.         itsRemotePort = theOpenParam.csParam.open.remotePort;
  289.         itsLocalIP = theOpenParam.csParam.open.localHost;
  290.         itsLocalPort = theOpenParam.csParam.open.localPort;
  291.     }
  292.     else
  293.         HandleOpenFailed(connectionExists);
  294.     
  295. }
  296.  
  297.  
  298. //***********************************************************
  299.  
  300. void CTCPStream::Close()
  301.  
  302.     // Signal that the connection should be closed. The stream’s owner should wait until it
  303.     // receives the tcpStreamClosed message before disposing of the stream object.
  304.     // This will allow time for the host to close gracefully.
  305.  
  306. {
  307.     TCPiopb    theCloseParam;
  308.     OSErr    theResult;
  309.  
  310.         if ((hasSessionOpen && (!pendingAbort) && (!pendingClose)) || remoteClose) {
  311.         pendingClose = true;
  312.         pendingOpen = remoteClose = false;
  313.         theCloseParam.csParam.close.ulpTimeoutValue = itsULPtimeout;
  314.         theCloseParam.csParam.close.ulpTimeoutAction = itsULPaction;
  315.         theCloseParam.csParam.close.validityFlags = itsValidityFlag;
  316.         theCloseParam.csParam.close.userDataPtr = (Ptr) this;
  317.         theResult = DoAsyncCall(TCPClose, &theCloseParam);
  318.         if ((theResult != noErr) && (theResult != connectionDoesntExist))
  319.             HandleTCPError(theResult, TCPClose);
  320.     }
  321. }
  322.  
  323.  
  324. //***********************************************************
  325.  
  326. void CTCPStream::Abort()
  327.  
  328.     // Cancel the connection immediately. This call is performed synchronously. USE THIS CALL
  329.     // WITH CAUTION! MacTCP seems to get quite confused when a session is closed and
  330.     // data hasn’t all been received.
  331.  
  332. {
  333.     TCPiopb    theAbortParam;
  334.     OSErr    theResult;
  335.  
  336.     if ((hasSessionOpen || pendingOpen) && (!pendingAbort)) {
  337.         pendingAbort = true;
  338.         pendingOpen = pendingClose = remoteClose = false;
  339.         theAbortParam.csParam.abort.userDataPtr = (Ptr) this;
  340.         theResult = DoSyncCall(TCPAbort, &theAbortParam);
  341.         if (theResult)
  342.             HandleTCPError(theResult, TCPAbort);
  343.         else
  344.             receivedClose = true;
  345.     }
  346. }
  347.  
  348.  
  349. //***********************************************************
  350.  
  351. void CTCPStream::NoCopyRcv
  352.     (rdsEntry*    itsRDS,            // receive data structure (see TCP dev guide, p30)
  353.     b_16            itsRDSSize,        // number of entries in RDS
  354.     b_16            itsTimeOut)        // command timeout value in seconds (0 = infinite)
  355.  
  356.     // Receive data without copying from TCP’s internal buffers. The completion routine in
  357.     // CTCPAsyncCall will take care of returning the RDS automatically. IF YOU ARE USING
  358.     // AUTO-RECEIVE, DO NOT CALL THIS METHOD!
  359.  
  360. {
  361.     TCPiopb    theRcvParam;
  362.     OSErr    theResult;
  363.  
  364.     theRcvParam.csParam.receive.commandTimeoutValue = itsTimeOut;
  365.     theRcvParam.csParam.receive.rdsPtr = (Ptr) itsRDS;
  366.     theRcvParam.csParam.receive.rdsLength = itsRDSSize;
  367.     if (itsRDSSize > autoReceiveMax)
  368.         theRcvParam.csParam.receive.rdsLength = autoReceiveMax;
  369.     theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
  370.     theRcvParam.csParam.open.options[36] = (Byte) itsAutoReceiveSize;
  371.  
  372.     theResult = DoAsyncCall(TCPNoCopyRcv, &theRcvParam);
  373.     if ((theResult != noErr) && (theResult != connectionClosing))
  374.         HandleTCPError(theResult, TCPNoCopyRcv);
  375.  
  376. }
  377.  
  378.  
  379. //***********************************************************
  380.  
  381. void CTCPStream::BfrReturn
  382.     (Ptr itsRDS)                // the RDS structure to return
  383.  
  384.     // Return a receive buffer to MacTCP used by the NoCopyRcv method. You should never need
  385.     // to call this method, since it is done automatically by the CTCPAsyncCall::Dispatch()
  386.     // method which handles all asynchronous completions.
  387.  
  388. {
  389.     TCPiopb theRcvParam;
  390.  
  391.  
  392.     // reject attempt to return buffers after the session is closed…
  393.     //    MacTCP has done it already (I think)
  394.  
  395.     if (hasSessionOpen) {
  396.         theRcvParam.csParam.receive.rdsPtr = itsRDS;
  397.         theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
  398.     
  399.         DoSyncCall(TCPRcvBfrReturn, &theRcvParam);
  400.                                 // ignore errors — this is bad practice, but…
  401.     }
  402.  
  403. }
  404.  
  405.  
  406. //***********************************************************
  407.  
  408. void CTCPStream::Send
  409.     (wdsEntry*    itsWDS,            // write data structure (see TCP dev guide, p30)
  410.     b_16            itsTimeOut,        // command timeout value in seconds (0 = infinite)
  411.     Boolean        disposeWDS,        // dispose of WDS and data structure when completed
  412.     Boolean        notifyWhenDone)    // notify endpoint object when send operation completes
  413.                                 //     (notification via HandleDataSent or HandleSendFailed)
  414.  
  415.     // Send data on the TCP stream using the Write Data Structure (WDS) structure. The
  416.     // asynchronous completion routine can optionally dispose (DisposPtr) of the WDS and its
  417.     // associated data structures once the write operation is completed. If so, the buffers you
  418.     // provide must be expendable.
  419.  
  420. {
  421.     TCPiopb    theSendParam;
  422.     OSErr    theResult;
  423.  
  424.  
  425.     // ensure that at least one byte is being sent
  426.     
  427.     
  428.     if ((*itsWDS).length == 0)
  429.         return;
  430.     
  431.  
  432.     // fill in parms to send call
  433.     
  434.     theSendParam.csParam.send.ulpTimeoutValue = itsTimeOut;
  435.     theSendParam.csParam.send.ulpTimeoutAction = itsULPaction;
  436.     theSendParam.csParam.send.validityFlags = itsValidityFlag;
  437.     theSendParam.csParam.send.pushFlag = sendNextPush;
  438.     theSendParam.csParam.send.urgentFlag = sendNextUrgent;
  439.     theSendParam.csParam.send.wdsPtr = (Ptr) itsWDS;
  440.     theSendParam.csParam.send.userDataPtr = (Ptr) this;
  441.     
  442.     theSendParam.csParam.open.timeToLive = notifyWhenDone;
  443.     theSendParam.csParam.open.security = disposeWDS;
  444.                     // ^^^^ this isn’t pretty, but it’s efficient
  445.  
  446.     sendNextPush = false;
  447.     sendNextUrgent = 0;
  448.  
  449.  
  450.     // perform the call
  451.     
  452.     theResult = DoAsyncCall(TCPSend, &theSendParam);
  453.     if (theResult)
  454.         HandleTCPError(theResult, TCPSend);
  455. }
  456.  
  457.  
  458. //***********************************************************
  459.  
  460. void CTCPStream::Receive
  461.     (Ptr        theData,            // receive buffer
  462.     b_16        itsDataSize,        // size of receive buffer in bytes
  463.     b_16        itsTimeOut)        // command timeout (in seconds, 0 = infinite)
  464.  
  465.     // Receive data and copy to a user buffer. Should not be used if auto-receive
  466.     // is active.
  467.  
  468. {
  469.     TCPiopb    theRcvParam;
  470.     OSErr    theResult;
  471.  
  472.     theRcvParam.csParam.receive.commandTimeoutValue = itsTimeOut;
  473.     theRcvParam.csParam.receive.rcvBuff = theData;
  474.     theRcvParam.csParam.receive.rcvBuffLen = (unsigned short) itsDataSize;
  475.     theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
  476.  
  477.     theResult = DoAsyncCall(TCPRcv, &theRcvParam);
  478.     if (theResult)
  479.         HandleTCPError(theResult, TCPRcv);
  480. }
  481.  
  482.  
  483. //***********************************************************
  484.  
  485. void CTCPStream::Status
  486.     (TCPStatusPB* theStatusBlock)        // user buffer for TCP status info
  487.  
  488.     // Returns a large pile of information about the TCP connection.
  489.  
  490. {
  491.     TCPiopb    theStatusParam;
  492.     short    i;
  493.     Ptr        p;
  494.     OSErr    result;
  495.     
  496.     result = DoSyncCall(TCPStatus, &theStatusParam);
  497.     ::BlockMoveData(&theStatusParam.csParam, theStatusBlock, 66);
  498.     if ((result) || (theStatusParam.ioResult != noErr)) {
  499.         i = 0;
  500.         p = (Ptr) theStatusBlock;
  501.         while (i++<66)
  502.             *(p++) = 0;
  503.     }
  504. }
  505.  
  506.  
  507. //***********************************************************
  508.  
  509. b_16 CTCPStream::ConnectionState()
  510.  
  511.     // Returns the current status of the MacTCP stream. For definitions of the state codes,
  512.     // see TCP reference manual, p56.
  513.  
  514. {
  515.     TCPStatusPB theStatusBlock;
  516.     
  517.     Status(&theStatusBlock);
  518.     return theStatusBlock.connectionState;
  519. }
  520.  
  521.  
  522. // -- specialized functions for sending data --
  523.  
  524. //***********************************************************
  525.  
  526. void CTCPStream::SendBfrCpy
  527.     (const void*        theData,            // the bytes to read
  528.     unsigned short    theDataSize)        // number of bytes to read
  529.  
  530.     // Send a range of bytes to the TCP stream. Prepares a WDS for the Send method and copies
  531.     // the bytes from the caller’s buffer to a data buffer which can persist beyond the procedure
  532.     // or object which generated the data.
  533.  
  534. {
  535.     wdsEntry*    itsWDS = nil;
  536.     Ptr            itsNewBuffer = nil;
  537.     
  538.  
  539.     // make sure there’s something to send
  540.  
  541.     if (theDataSize == 0)
  542.         return;
  543.     
  544.     Try_ {
  545.     
  546.         // create the two buffers
  547.  
  548.         itsWDS = (wdsEntry*) ::NewPtr(8);            // WDS with room for one entry
  549.         itsNewBuffer = ::NewPtr(theDataSize);
  550.  
  551.  
  552.         // copy stuff to new buffer
  553.         
  554.         ::BlockMoveData(theData, itsNewBuffer, theDataSize);
  555.         (*itsWDS).length = theDataSize;
  556.         (*itsWDS).ptr = itsNewBuffer;
  557.         (*(++itsWDS)).length = 0;
  558.         --itsWDS;
  559.  
  560.  
  561.         // send data, using default timeout value
  562.         
  563.         Send(itsWDS, itsULPtimeout, true, false);
  564.                                             // TurboTCP 2.0 NOTE: completion of Send
  565.                                             //        is no longer called
  566.     }
  567.     
  568.     Catch_(err) {
  569.         ::DisposePtr((Ptr) itsWDS);
  570.         ::DisposePtr(itsNewBuffer);
  571.         ThrowSame_;
  572.     }
  573.     EndCatch_;
  574.  
  575. }
  576.  
  577.  
  578. //***********************************************************
  579.  
  580. void CTCPStream::SendBfrNoCpy
  581.     (const void*        theData,            // the bytes to send
  582.     unsigned short    theDataSize,        // number of bytes to send
  583.     Boolean            disposeWhenDone,    // true to dispose of the buffer (via DisposePtr)
  584.                                     //    when completed (use only if this pointer was
  585.                                     //    allocated as a pointer, not a handle deref)
  586.     Boolean            notifyWhenDone)    // true to notify the endpoint object when completed
  587.                                     //    (via HandleDataSent or HandleSendFailed)
  588.  
  589.     // Send a range of bytes to the TCP stream. Prepares a WDS for the Send method. Does not
  590.     // copy the bytes.
  591.  
  592. {
  593.     wdsEntry *itsWDS = nil;
  594.     
  595.  
  596.     // make sure there’s something to send
  597.  
  598.     if (theDataSize == 0)
  599.         return;
  600.     
  601.     Try_ {
  602.  
  603.         // create the WDS buffer & fill it in
  604.  
  605.         itsWDS = (wdsEntry*) ::NewPtr(8);            // WDS with room for one entry
  606.         
  607.         (*itsWDS).length = theDataSize;
  608.         (*itsWDS).ptr = (Ptr) theData;
  609.         (*(++itsWDS)).length = 0;
  610.         --itsWDS;
  611.  
  612.  
  613.         // send data, using default timeout value
  614.         
  615.         Send(itsWDS, itsULPtimeout, disposeWhenDone, notifyWhenDone);
  616.     }
  617.     
  618.     Catch_(err) {
  619.         ::DisposePtr((Ptr) itsWDS);
  620.         ThrowSame_;
  621.     }
  622.     EndCatch_;
  623.  
  624. }
  625.  
  626.  
  627. // -- notification routines --
  628.  
  629. //***********************************************************
  630.  
  631. void CTCPStream::HandleDataSent            // private method
  632.     (wdsEntry* WDSPtr,                // the WDS structure
  633.     Boolean disposeWhenDone,                // true to dispose of memory units
  634.     Boolean notifyWhenDone)                // true to notify endpoint object
  635.  
  636.     // Respond to successful data sending. Notifies endpoint object and disposes of data if
  637.     // requested. Disposes of WDS structure.
  638.  
  639. {
  640.     wdsEntry* theDisposePtr = WDSPtr;
  641.     while ((*theDisposePtr).length) {
  642.         if ((notifyWhenDone) && (itsEndpoint))
  643.             itsEndpoint->HandleDataSent((*theDisposePtr).ptr, (*theDisposePtr).length);
  644.         if (disposeWhenDone)
  645.             ::DisposePtr((*theDisposePtr).ptr);
  646.         theDisposePtr++;
  647.     }
  648.     ::DisposePtr((Ptr) WDSPtr);
  649. }
  650.  
  651.  
  652. //***********************************************************
  653.  
  654. void CTCPStream::HandleSendFailed            // private method
  655.     (wdsEntry*    WDSPtr,                // the WDS structure
  656.     Boolean        disposeWhenDone,        // true to dispose of memory chunks
  657.     Boolean        notifyWhenDone,        // true to notify endpoint object
  658.     OSErr        theResultCode)            // the reason for failure
  659.  
  660.     // Respond to failure to send data. Notifies endpoint object and disposes of data if requested.
  661.     // Disposes of WDS structure.
  662.  
  663. {
  664.     // send error notification (once only)
  665.  
  666.     if (itsEndpoint)
  667.         itsEndpoint->HandleTCPError(theResultCode, TCPSend);
  668.     else    
  669.         disposeWhenDone = true;
  670.  
  671.  
  672.     // notify and dispose each data packet in WDS
  673.     
  674.     wdsEntry* theDisposePtr = WDSPtr;
  675.     while ((*theDisposePtr).length) {
  676.         if ((notifyWhenDone) && (itsEndpoint))
  677.             itsEndpoint->HandleSendFailed((*theDisposePtr).ptr, (*theDisposePtr).length, theResultCode);
  678.         if (disposeWhenDone)
  679.             ::DisposePtr((*theDisposePtr).ptr);
  680.         theDisposePtr++;
  681.     }
  682.     ::DisposePtr((Ptr) WDSPtr);
  683.  
  684. }
  685.  
  686.  
  687. // -- private methods for initiating TCP calls -- 
  688.  
  689. //***********************************************************
  690.  
  691. OSErr CTCPStream::DoAsyncCall            // private method
  692.     (b_16    theCsCode,                // TCP operation code
  693.     TCPiopb*    theParamBlockPtr)            // parameter block for TCP call
  694.  
  695.     // Creates a CTCPAsyncCall object and uses it to execute a TCP Device Manager call
  696.     // asynchronously. Does not wait for completion. Fails if not enough memory to
  697.     // create call object.
  698.  
  699.     // Returns result code (+1 is not returned).
  700.  
  701. {
  702.     OSErr            theResult;
  703.     CTCPAsyncCall*    theCall;
  704.  
  705.  
  706.     // make sure a stream was opened
  707.     
  708.     if (macTCPStream)
  709.         (*theParamBlockPtr).tcpStream = (StreamPtr) macTCPStream;
  710.     else
  711.         return invalidStreamPtr;
  712.  
  713.  
  714.     // create call object
  715.     
  716.     theCall = new CTCPAsyncCall(*this);
  717.     theResult = theCall->DoAsyncCall(theCsCode, theParamBlockPtr);
  718.     if (theResult == inProgress)
  719.         ::Enqueue((QElemPtr) &theCall->itsStreamEntry, &itsAsyncCalls);
  720.     return (theResult == inProgress) ? noErr : theResult;
  721.     
  722. }
  723.  
  724.  
  725. //***********************************************************
  726.  
  727. OSErr CTCPStream::DoSyncCall            // private method
  728.     (b_16    theCsCode,                // TCP operation code
  729.     TCPiopb*    theParamBlockPtr)            // parameter block for TCP call
  730.  
  731.     // Fills in the standard parameters for a TCP Device Manager call and executes the call.
  732.     // Waits for completion of the call.
  733.  
  734.     // Returns result code (+1 is not returned).
  735.  
  736. {
  737.  
  738.     // make sure a stream was opened
  739.     
  740.     if (macTCPStream)
  741.         (*theParamBlockPtr).tcpStream = (StreamPtr) macTCPStream;
  742.     else
  743.         return invalidStreamPtr;
  744.  
  745.  
  746.     // perform the call
  747.     
  748.     (*theParamBlockPtr).ioCompletion = (TCPIOCompletionUPP) nil;
  749.     (*theParamBlockPtr).ioCRefNum = CTCPDriver::gTCPDriver->GetTCPRefNum();
  750.     (*theParamBlockPtr).csCode = theCsCode;
  751.     ::PBControlSync((ParmBlkPtr) theParamBlockPtr);
  752.     return (*theParamBlockPtr).ioResult;
  753.     
  754. }
  755.  
  756.  
  757. //***********************************************************
  758.  
  759. void CTCPStream::IssueAutoReceive()        // private method
  760.  
  761.     // Issue a NoCopyRcv command to grab the next batch of data. Does nothing if auto-receive
  762.     // is not enabled.
  763.  
  764. {
  765.     // create a new RDS with room for # of entries requested & send it to TCP
  766.     
  767.     Ptr newRDS;
  768.     if (hasSessionOpen && itsAutoReceiveSize) {
  769.         newRDS = ::NewPtr((itsAutoReceiveSize*6)+2);
  770.         ThrowIfNil_(newRDS);
  771.         NoCopyRcv((rdsEntry*) newRDS, itsAutoReceiveSize, 0);
  772.     }
  773. }
  774.  
  775.  
  776. //***********************************************************
  777.  
  778. void CTCPStream::ProcessNotify()
  779.  
  780.     // Respond to notifications received by the ASR during interrupt time. This routine is free
  781.     // of interrupt-level constraints.
  782.  
  783. {
  784.     // no longer in notify queue
  785.     
  786.     pendingNotify = false;
  787.     
  788.     
  789.     // if terminated, inform the stream owner & dispose (if appropriate)
  790.     //    clear all other notifications (they no longer apply)
  791.  
  792.     if (notifTerminate) {
  793.         HandleTerminated(notifTermReason);
  794.         hasSessionOpen = pendingOpen = pendingClose = 
  795.             remoteClose = pendingAbort = notifClosing = notifTimeout =
  796.             notifDataArrived = notifUrgent = false;
  797.         receivedClose = receivedTerminate = true;
  798.         if (disposeOnTerminate)
  799.             PostponeDispose();
  800.     }
  801.  
  802.  
  803.     // if closed, inform stream owner, wait for termination before disposing (if appropriate)
  804.  
  805.     if (notifClosing) {
  806.         HandleClosing(remoteClose);
  807.         receivedClose = true;
  808.         notifClosing = false;
  809.         if (disposeOnTerminate)
  810.             PostponeDispose();
  811.     }
  812.  
  813.  
  814.     // if urgent notification, flag beginning of urgent data
  815.  
  816.     if (notifUrgent) {
  817.         if (!rcvUrgent) {
  818.             RcvUrgentBegin();
  819.             HandleUrgentBegin();
  820.         }
  821.         notifUrgent = false;
  822.     }
  823.  
  824.  
  825.     // tell stream owner about other notifications, TurboTCP doesn’t respond to these
  826.  
  827.     if (notifTimeout) {
  828.         HandleTimeout();
  829.         notifTimeout = false;
  830.     }
  831.     
  832.     if (notifDataArrived) {
  833.         HandleUnexpectedData();
  834.         notifDataArrived = false;
  835.     }
  836.     
  837.     if (notifICMP) {
  838.         HandleICMP(¬ifICMPreport);
  839.         notifICMP = false;
  840.     }
  841.  
  842. }
  843.  
  844.  
  845. //***********************************************************
  846.  
  847. void CTCPStream::ProcessAsyncCompletion
  848.     (CTCPAsyncCall* theCall)                // the call which was completed
  849.  
  850.     // Respond to the completion of an asynchronous call. Removes the async call object from
  851.     // the list of outstanding calls.
  852.  
  853. {
  854.     ::Dequeue((QElemPtr) &theCall->itsStreamEntry, &itsAsyncCalls);
  855. }
  856.  
  857.  
  858. #ifndef __MWERKS__
  859.     //#pragma options(!profile)
  860. #else
  861.     #pragma profile off
  862. #endif
  863.  
  864.  
  865. //************* **********************************************
  866.  
  867. void CTCPStream::PostponeDispose()
  868.  
  869.     // Add this stream object to the delayed disposal queue.
  870.     // *** Runs at interrupt level. ***
  871.     
  872. {
  873.     if (pendingDispose)                    // ignore this if we’re already in disposal queue
  874.         return;
  875.     pendingDispose = true;
  876.     disposeOnTerminate = true;
  877.     ::Enqueue((QElemPtr) &qDisposeEntry, &(CTCPDriver::gTCPDriver->asyncQueue));
  878. }
  879.  
  880.  
  881. //***********************************************************
  882.  
  883. void CTCPStream::PostponeNotify                // protected method
  884.     (b_16                eventCode,        // event code (see TCP dev guide, p37)
  885.     b_16                    terminReason,        // reason for termination (if applicable)
  886.     struct ICMPReport*    icmpMsg)            // ICMP report (if applicable)
  887.  
  888.     // The asynchronous notification routine (ASR). Receives notification of events for all TCP
  889.     // streams. Dispatches the notification to the proper method of the CTCPStream object. This
  890.     // routine is installed for all TCP streams created by the CTCPStream object.
  891.     
  892.     // This method merely logs the kind of notification and adds this stream to the list of streams
  893.     // to be processed next time through the event loop.
  894.     
  895.     // *** Runs at interrupt level. ***
  896.  
  897. {
  898.  
  899.     // reject notifications for unexpected data if auto-receiving
  900.     
  901.     if ((itsAutoReceiveSize > 0) && (eventCode == TCPDataArrival))
  902.         return;
  903.  
  904.  
  905.     // install this stream in asynchronous events queue
  906.     //    CTCPDriver::ProcessNetEvents will pick it up later
  907.  
  908.     if (!pendingNotify) {
  909.         ::Enqueue((QElemPtr) &qNotifyEntry, &(CTCPDriver::gTCPDriver->asyncQueue));
  910.         pendingNotify = true;
  911.     }
  912.  
  913.  
  914.     // record the event type
  915.  
  916.     switch (eventCode) {
  917.         case TCPClosing:
  918.             remoteClose = remoteClose || !pendingClose;
  919.             notifClosing = pendingClose = true;
  920.             break;
  921.  
  922.         case TCPULPTimeout:
  923.             notifTimeout = true;
  924.             break;
  925.  
  926.         case TCPTerminate:
  927.             notifTerminate = true;
  928.             notifTermReason = terminReason;
  929.             break;
  930.  
  931.         case TCPDataArrival:
  932.             notifDataArrived = true;
  933.             break;
  934.  
  935.         case TCPUrgent:
  936.             notifUrgent = true;
  937.             break;
  938.  
  939.         case TCPICMPReceived:
  940.             notifICMP = true;
  941.             ::BlockMoveData(icmpMsg, ¬ifICMPreport, 24);
  942.             break;
  943.     }
  944.  
  945. }
  946.  
  947.  
  948. //***********************************************************
  949.  
  950. pascal void CTCPStream::NotifyProc            // private static method
  951.     (StreamPtr            tcpStream,        // the TCP stream in question
  952.     unsigned short        eventCode,        // TCP event code (see TCP dev guide, p37)
  953.     Ptr                    userDataPtr,        // user data pointer (in this case, the "this" pointer)
  954.     unsigned short        terminReason,        // reason for termination (see TCP dev guide, p37)
  955.     struct ICMPReport*    icmpMsg)            // ICMP report (if applicable)
  956.  
  957.     // The asynchronous notification routine (ASR). This static method just decodes the
  958.     // userDataPtr and calls PostponeNotify for the object named. This routine is the standard
  959.     // ASR for all TCP streams created by the CTCPStream object.
  960.  
  961. {
  962.     CTCPStream* theTCPStream = (CTCPStream*) userDataPtr;
  963.     theTCPStream->PostponeNotify(eventCode, terminReason, icmpMsg);
  964. }
  965.